package com.rmfyzxfw.caseprocess.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.rmfyzxfw.caseprocess.boot.result.page.Pagination;
import com.rmfyzxfw.caseprocess.enums.DataTypeEnum;
import com.rmfyzxfw.caseprocess.enums.DynamicDatabaseTypeEnum;
import com.rmfyzxfw.caseprocess.enums.EnableStatusEnum;
import com.rmfyzxfw.caseprocess.enums.True1False0;
import com.rmfyzxfw.caseprocess.manager.CaseManager;
import com.rmfyzxfw.caseprocess.mapper.*;
import com.rmfyzxfw.caseprocess.model.cmd.*;
import com.rmfyzxfw.caseprocess.model.converter.ProcessConverter;
import com.rmfyzxfw.caseprocess.model.converter.ProcessDataConverter;
import com.rmfyzxfw.caseprocess.model.dto.RuleItemDTO;
import com.rmfyzxfw.caseprocess.model.param.CaseProcessDataNodeDataParam;
import com.rmfyzxfw.caseprocess.model.param.ProcessListParam;
import com.rmfyzxfw.caseprocess.model.po.Process;
import com.rmfyzxfw.caseprocess.model.po.*;
import com.rmfyzxfw.caseprocess.model.vo.*;
import com.rmfyzxfw.caseprocess.service.CaseProcessService;
import com.rmfyzxfw.caseprocess.service.ProcessService;
import com.rmfyzxfw.caseprocess.util.DateUtil;
import com.rmfyzxfw.caseprocess.util.DynamicDataSourceUtil;
import com.rmfyzxfw.caseprocess.util.RuleExpressionUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.google.common.collect.Lists.newArrayList;

/**
 * @author GYW
 * @description 针对表【t_process(流程)】的数据库操作Service实现
 * @createDate 2024-08-14 16:11:02
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ProcessServiceImpl extends ServiceImpl<ProcessMapper, Process>
        implements ProcessService {

    private final ProcessConverter processConverter;
    private final ProcessDataConverter processDataConverter;
    private final ProcessDataMapper processDataMapper;
    private final CaseProcessService caseProcessService;
    private final EngineDataDynamicTableMySqlMapper engineDataDynamicTableMySqlMapper;
    private final EngineDataDynamicTableOracleMapper engineDataDynamicTableOracleMapper;
    private final ZXCaseMapper zxCaseMapper;

    private final CaseProcessNodeMapper caseProcessNodeMapper;
    private final CaseDataNodeMapper caseDataNodeMapper;
    private final CaseDataNodeStatusMapper caseDataNodeStatusMapper;
    private final ProcessMapper processMapper;
    private final StatusMapper statusMapper;
    private final DataNodeMapper dataNodeMapper;
    private final DataNodeItemRelMapper dataNodeItemRelMapper;
    private final DataItemMapper dataItemMapper;
    private final DataFieldMapper dataFieldMapper;
    private final DynamicDataSourceMapper dynamicDataSourceMapper;
    private final CaseManager caseManager;
    private final FymcMapper fymcMapper;

    @Override
    public void addProcess(ProcessCmd cmd) {
        // 同一个法院 统一数据源（审判、执行）下的案件类型（金钱给付、财产保全）可以多个
        Process process = processConverter.cmd2do(cmd);
        process.setStatus(EnableStatusEnum.DISABLE.getCode());
        process.setProcessId(IdUtil.simpleUUID());
        this.save(process);
    }

    @Override
    public void editProcess(ProcessEditCmd cmd) {
        // 从列表获取回填信息
        Process process = processConverter.cmd2do(cmd);
        this.update(process, Wrappers.<Process>lambdaQuery().eq(Process::getProcessId, process.getProcessId()));
    }

    @Override
    public ProcessVO getProcessInfo(String processId) {
        Process process = this.getOne(Wrappers.<Process>lambdaQuery().eq(Process::getProcessId, processId));
        ProcessVO processVO = processConverter.do2vo(process);
        List<ProcessData> processDataList = processDataMapper.selectList(Wrappers.<ProcessData>lambdaQuery().eq(ProcessData::getProcessId, processId)
                .select(ProcessData::getProcessDataId, ProcessData::getVersion, ProcessData::getRelease).orderByDesc(ProcessData::getCreateTime));
        List<ProcessDataListVO> processDataListVOS = processDataConverter.dos2vos(processDataList);
        processVO.setProcessDataListVOS(processDataListVOS);

        return processVO;
    }

    @Override
    public List<ProcessNodeVO> getProcessConfigData(String processDataId) {
        ProcessData processData = processDataMapper.selectOne(Wrappers.<ProcessData>lambdaQuery().eq(ProcessData::getProcessDataId, processDataId));
        List<ProcessNodeVO> processNodeVOS = JSON.parseArray(processData.getData(), ProcessNodeVO.class);
        List<ProcessNodeVO> collect = processNodeVOS.stream().sorted(Comparator.comparingInt(ProcessNodeVO::getSort)).collect(Collectors.toList());
        return collect;
    }

    @Override
    public Pagination<ProcessListVO> processList(ProcessListParam param) {
        // 根据案件查询 必须传数据源类型 流程必须已启用
        List<String> fydms = Lists.newArrayList();
        if (StringUtils.isNotBlank(param.getAh()) && Objects.nonNull(param.getDataSourceType())) {
            // 切换数据源
            DynamicDataSource dynamicDataSource = dynamicDataSourceMapper.selectOne(Wrappers.<DynamicDataSource>lambdaQuery().eq(DynamicDataSource::getDataSourceType, param.getDataSourceType()));
            ZXCaseInfo zxCaseInfo = DynamicDataSourceUtil.route(dynamicDataSource.getDynamicDataSourceId()
                    , () -> zxCaseMapper.selectOne(Wrappers.<ZXCaseInfo>lambdaQuery().eq(ZXCaseInfo::getAh, param.getAh()).last(" offset 0 rows fetch next 1 rows only")));
            if(Objects.isNull(zxCaseInfo)){
                return Pagination.empty();
            }
            caseManager.setCaseType(zxCaseInfo);
            if (StringUtils.isEmpty(zxCaseInfo.getCaseType())) {
               return Pagination.empty();
            }
            Fymc fymc = fymcMapper.selectOne(Wrappers.<Fymc>lambdaQuery().eq(Fymc::getFydmHy, zxCaseInfo.getFydm()));
            param.setCaseType(zxCaseInfo.getCaseType());
            param.setStatus(True1False0.TRUE.getCode());
            param.setCourtId(zxCaseInfo.getFydm());
            fydms.add(fymc.getGydm());
        }
        fydms.add(param.getCourtId());
        Page<Process> page = new Page<>(param.getPageNum(), param.getPageSize());
        Page<Process> processPage = this.page(page, Wrappers.<Process>lambdaQuery()
                .eq(Objects.nonNull(param.getStatus()), Process::getStatus, param.getStatus())
                .in(StringUtils.isNotBlank(param.getCourtId()), Process::getCourtId, fydms)
                .eq(StringUtils.isNotBlank(param.getCaseType()), Process::getCaseType, param.getCaseType())
                .eq(Objects.nonNull(param.getDataSourceType()), Process::getDataSourceType, param.getDataSourceType())
                .like(StringUtils.isNotBlank(param.getProcessName()), Process::getProcessName, param.getProcessName())
                .orderByDesc(Process::getId)
        );
        //传入案件查询时可能涉及本院和高院
        if (fydms.size() > 1) {
            // 分成本院和高院两组，如果本院有流程则使用本院，没有则使用高院配置的流程
            Map<String, List<Process>> collect = processPage.getRecords().stream().collect(Collectors.groupingBy(Process::getCourtId));
            List<Process> processes = collect.get(fydms.get(1));
            if (CollectionUtils.isEmpty(processes)) {
                processes = collect.get(fydms.get(0));
            }
            processPage.setRecords(processes);
        }

        return Pagination.build(processPage, processConverter::dos2vos);
    }

    @Override
    @Transactional
    public void processConfig(ProcessConfigCmd cmd) {
        //TODO A数据节点作为B数据节点中规则表达式条件必须满足：A、B节点都在同一个流程 ,或者A在子流程 当前流程如果移除了节点A，B中的表达式会移除，但是子流程的如何处理？
        List<ProcessNodeCmd> processNodeCmds = cmd.getProcessNodes();
        // 流程发布至少要有一个流程节点和一个数据节点
        if (True1False0.TRUE.getCode().equals(cmd.getRelease())) {
            if (CollectionUtils.isEmpty(processNodeCmds)) {
                throw new RuntimeException("流程发布至少要有一个流程节点");
            }
        }

        // 参数校验 规则表达式校验

        // 校验流程名称是否重复
        validProcessName(processNodeCmds);
        // 校验流程节点及数据节点规则表达式
        validRuleExpression(processNodeCmds);
        // 判断是否保存新版本
        // 1.processDataId 为空  直接保存新版本
        if (StringUtils.isBlank(cmd.getProcessDataId())) {
            ProcessData processDataLast = processDataMapper.selectOne(Wrappers.<ProcessData>lambdaQuery()
                    .eq(ProcessData::getProcessId, cmd.getProcessId())
                    .orderByDesc(ProcessData::getId)
                    .last("limit 1"));
            ProcessData processData = new ProcessData();
            processData.setProcessId(cmd.getProcessId());
            processData.setProcessDataId(IdUtil.simpleUUID());
            processData.setVersion(Objects.isNull(processDataLast) ? 1 : processDataLast.getVersion() + 1);
            processData.setData(JSON.toJSONString(processNodeCmds));
            processData.setRelease(cmd.getRelease());
            processDataMapper.insert(processData);
            Process process = new Process();
            process.setShowProcessDataId(processData.getProcessDataId());
            processMapper.update(process, Wrappers.<Process>lambdaQuery().eq(Process::getProcessId, cmd.getProcessId()));
            return;
        }

        // 2.processDataId 不为空  判断是否已发版，如果已发版不允许修改
        ProcessData processData = processDataMapper.selectOne(Wrappers.<ProcessData>lambdaQuery()
                .eq(ProcessData::getProcessDataId, cmd.getProcessDataId()));

        if (True1False0.TRUE.getCode().equals(processData.getRelease())) {
            throw new RuntimeException("已发布流程不允许修改");
        }
        processData.setData(JSON.toJSONString(cmd.getProcessNodes()));
        processData.setRelease(cmd.getRelease());
        processDataMapper.updateById(processData);
        Process process = new Process();
        process.setShowProcessDataId(processData.getProcessDataId());
        processMapper.update(process, Wrappers.<Process>lambdaQuery().eq(Process::getProcessId, cmd.getProcessId()));

    }

    @Override
    public void processEnable(ProcessEnableCmd cmd) {
        Process p = new Process();
        p.setProcessDataId(cmd.getProcessDataId());
        ProcessData processData = processDataMapper.selectOne(Wrappers.<ProcessData>lambdaQuery()
                .eq(ProcessData::getProcessDataId, cmd.getProcessDataId()));
        if (True1False0.FALSE.getCode().equals(processData.getRelease())) {
            throw new RuntimeException("该流程未发布");
        }
        update(p, Wrappers.<Process>lambdaQuery().eq(Process::getProcessId, cmd.getProcessId()).last("limit 1"));
        // TODO 去更新对应案件的流程节点状态 1.添加任务  2.异步执行(事务)  每次都是插入或更新、是否还需要一致性？
    }

    @Override
    public ProcessStatusVO updateAndGetStatus(CaseProcessStatusCmd cmd) {
        Process process = processMapper.selectOne(Wrappers.<Process>lambdaQuery().eq(Process::getProcessId, cmd.getProcessId()).last(" limit 1"));
        // 切换数据源
        DynamicDataSource dynamicDataSource = dynamicDataSourceMapper.selectOne(Wrappers.<DynamicDataSource>lambdaQuery().eq(DynamicDataSource::getDataSourceType, process.getDataSourceType()));
        ZXCaseInfo zxCaseInfo = DynamicDataSourceUtil.route(dynamicDataSource.getDynamicDataSourceId()
                , () -> zxCaseMapper.selectOne(Wrappers.<ZXCaseInfo>lambdaQuery().eq(ZXCaseInfo::getAh, cmd.getAh()).last(" offset 0 rows fetch next 1 rows only")));
        // TODO 获取鱼骨图节点状态时是否传入流程id，只更新指定流程
        if(Objects.isNull(zxCaseInfo)){
            throw new RuntimeException("案件不存在 " + cmd.getAh());
        }
        caseProcessService.updateSingleCaseProcessStatus(zxCaseInfo);

        ProcessStatusVO processStatusVO = new ProcessStatusVO();
        processStatusVO.setProcessName(process.getProcessName());
        processStatusVO.setProcessId(process.getProcessId());
        processStatusVO.setProcessDataId(process.getProcessDataId());
        ProcessData processData = processDataMapper.selectOne(Wrappers.<ProcessData>lambdaQuery().eq(ProcessData::getProcessDataId, process.getProcessDataId()));
        List<ProcessNodeVO> processNodeVOS = JSON.parseArray(processData.getData(), ProcessNodeVO.class);

        List<ProcessStatusVO.ProcessNodeStatus> processNodeStatusList = newArrayList();
        processStatusVO.setProcessNodeStatusList(processNodeStatusList);
        // 获取流程节点状态
        for (ProcessNodeVO processNodeVO : processNodeVOS) {
            CaseProcessNode caseProcessNode = caseProcessNodeMapper.selectOne(Wrappers.<CaseProcessNode>lambdaQuery()
                    .eq(CaseProcessNode::getAh, zxCaseInfo.getAh())
                    .eq(CaseProcessNode::getProcessNodeId, processNodeVO.getProcessNodeId())
                    .eq(CaseProcessNode::getProcessId, process.getProcessId())
                    .eq(CaseProcessNode::getProcessDataId, process.getProcessDataId()));
            ProcessStatusVO.ProcessNodeStatus processNodeStatus = new ProcessStatusVO.ProcessNodeStatus();
            processNodeStatus.setProcessNodeId(processNodeVO.getProcessNodeId());
            processNodeStatus.setProcessNodeName(processNodeVO.getProcessNodeName());
            processNodeStatus.setLight(caseProcessNode.getStatus());
            List<ProcessStatusVO.DataNodeStatus> dataNodeStatusList = newArrayList();
            processNodeStatus.setDataNodeStatusList(dataNodeStatusList);
            // 单个流程节点状态
            processNodeStatusList.add(processNodeStatus);

            // 获取数据节点状态
            for (ProcessNodeVO.DataNodeVO dataNodeVO : processNodeVO.getDataNodes()) {
                ProcessStatusVO.DataNodeStatus dataNodeStatus = new ProcessStatusVO.DataNodeStatus();
                dataNodeStatus.setDataNodeId(dataNodeVO.getDataNodeId());
                dataNodeStatus.setDataNodeName(dataNodeVO.getDataNodeName());
                if(CollectionUtils.isEmpty(dataNodeVO.getDataNodeStatuses())){
                    dataNodeStatus.setLight(True1False0.FALSE.getCode());
                    dataNodeStatusList.add(dataNodeStatus);
                    continue;
                }
                CaseDataNode caseDataNode = caseDataNodeMapper.selectOne(Wrappers.<CaseDataNode>lambdaQuery()
                        .eq(CaseDataNode::getAh, zxCaseInfo.getAh())
                        .eq(CaseDataNode::getProcessId, process.getProcessId())
                        .eq(CaseDataNode::getProcessDataId, process.getProcessDataId())
                        .eq(CaseDataNode::getProcessNodeId, processNodeVO.getProcessNodeId())
                        .eq(CaseDataNode::getDataNodeId, dataNodeVO.getDataNodeId()));
                CaseDataNodeStatus caseDataNodeStatus = caseDataNodeStatusMapper.selectOne(Wrappers.<CaseDataNodeStatus>lambdaQuery()
                        .eq(CaseDataNodeStatus::getCaseDataNodeId, caseDataNode.getCaseDataNodeId())
                        .in(CaseDataNodeStatus::getStatusId, dataNodeVO.getDataNodeStatuses().stream().map(ProcessNodeVO.DataNodeStatusVO::getStatusId).collect(Collectors.toList()))
                        .eq(CaseDataNodeStatus::getLightStatus, True1False0.TRUE.getCode())
                        .orderByDesc(CaseDataNodeStatus::getStatusLevel)
                        .last("limit 1"));


                if (Objects.nonNull(caseDataNodeStatus)) {
                    dataNodeStatus.setLight(caseDataNodeStatus.getLightStatus());
                    Status status = statusMapper.selectOne(Wrappers.<Status>lambdaQuery().eq(Status::getStatusId, caseDataNodeStatus.getStatusId()));
                    dataNodeStatus.setColor(status.getColor());
                } else {
                    dataNodeStatus.setLight(True1False0.FALSE.getCode());
                }
                // 单个数据节点状态
                dataNodeStatusList.add(dataNodeStatus);
            }
        }

        return processStatusVO;
    }

    @Override
    public CaseProcessDataNodeDataVO getDataNodeData(CaseProcessDataNodeDataParam param) {
        CaseProcessDataNodeDataVO caseProcessDataNodeDataVO = new CaseProcessDataNodeDataVO();
        caseProcessDataNodeDataVO.setDataNodeId(param.getDataNodeId());
        List<String> dataItemIds = dataNodeItemRelMapper.selectList(Wrappers.<DataNodeItemRel>lambdaQuery()
                        .eq(DataNodeItemRel::getDataNodeId, param.getDataNodeId())
                        .select(DataNodeItemRel::getDataItemId))
                .stream()
                .map(DataNodeItemRel::getDataItemId)
                .collect(Collectors.toList());
        if(CollectionUtils.isEmpty(dataItemIds)){
            return caseProcessDataNodeDataVO;
        }
        List<DataItem> dataItems = dataItemMapper.selectList(Wrappers.<DataItem>lambdaQuery().in(DataItem::getDataItemId, dataItemIds));
        List<CaseProcessDataNodeDataVO.CaseDataItemVO> caseDataItemVOs = newArrayList();
        caseProcessDataNodeDataVO.setCaseDataItemVOs(caseDataItemVOs);
        for (DataItem dataItem : dataItems) {
            queryData(param, caseDataItemVOs, dataItem);
        }

        return caseProcessDataNodeDataVO;
    }

    /**
     * 查询单个数据项
     *
     * @param param
     * @param caseDataItemVOs
     * @param dataItem
     */
    private void queryData(CaseProcessDataNodeDataParam param, List<CaseProcessDataNodeDataVO.CaseDataItemVO> caseDataItemVOs, DataItem dataItem) {
        List<DataField> dataFields = dataFieldMapper.selectList(Wrappers.<DataField>lambdaQuery()
                .eq(DataField::getDataItemId, dataItem.getDataItemId()));
        CaseProcessDataNodeDataVO.CaseDataItemVO caseDataItemVO = new CaseProcessDataNodeDataVO.CaseDataItemVO();
        caseDataItemVO.setDataItemId(dataItem.getDataItemId());
        caseDataItemVO.setDataItemName(dataItem.getItemName());
        List<String> headers = newArrayList();
        // 固定字段顺序
        Map<String, String> headerMap = new LinkedHashMap<>();
        List<Map<String, Object>> data = getDirectData(param, dataItem, dataFields, headers, headerMap);
        List<List<Object>> values = getFinalValues(headerMap, data);
        caseDataItemVO.setHeaders(headers);
        caseDataItemVO.setValues(values);
        caseDataItemVOs.add(caseDataItemVO);
    }

    /**
     * 查询数据库
     *
     * @param param
     * @param dataItem
     * @param dataFields
     * @param headers
     * @param headerMap
     * @return
     */
    private List<Map<String, Object>> getDirectData(CaseProcessDataNodeDataParam param, DataItem dataItem, List<DataField> dataFields, List<String> headers, Map<String, String> headerMap) {
        StringBuilder sql = new StringBuilder("select ");
        buildQueryFields(dataFields, headers, headerMap, sql);
        // 切换数据源
        DynamicDataSource dynamicDataSource = dynamicDataSourceMapper.selectOne(Wrappers.<DynamicDataSource>lambdaQuery().eq(DynamicDataSource::getDataSourceType, dataItem.getDataSourceType()));
        ZXCaseInfo zxCaseInfo = DynamicDataSourceUtil.route(dynamicDataSource.getDynamicDataSourceId()
                , () -> zxCaseMapper.selectOne(Wrappers.<ZXCaseInfo>lambdaQuery().eq(ZXCaseInfo::getAh, param.getAh()).last(" offset 0 rows fetch next 1 rows only")));
        buildQueryCondition(dataItem, sql, zxCaseInfo);
        // hashmap乱序 表头和数据的值位置需要一致
        List<Map<String, Object>> data;
        if(DynamicDatabaseTypeEnum.MYSQL.equals(DynamicDatabaseTypeEnum.ofCode(dynamicDataSource.getDatabaseType()))){
            data = DynamicDataSourceUtil.route(dynamicDataSource.getDynamicDataSourceId(), () -> engineDataDynamicTableMySqlMapper.getBySql(sql.toString()));
        }else {
            data = DynamicDataSourceUtil.route(dynamicDataSource.getDynamicDataSourceId(), () -> engineDataDynamicTableOracleMapper.getBySql(sql.toString()));
        }
        return data;
    }


    /**
     * 按照查询顺序构建最终结果
     */
    private static List<List<Object>> getFinalValues(Map<String, String> headerMap, List<Map<String, Object>> data) {
        List<List<Object>> values = newArrayList();
        for (Map<String, Object> datum : data) {
            List<Object> rowValue = newArrayList();
            // 按照查询字段顺序返回结果 处理单行数据
            headerMap.forEach((k, v) -> {
                if(Objects.isNull(datum)){
                    return;
                }
                Object o = datum.get(k);
                if(Objects.isNull(o)){
                    return;
                }
                // 处理日期类型
                if (o instanceof LocalDate) {
                    rowValue.add(DateUtil.localDate2str((LocalDate) o));
                } else if (o instanceof LocalDateTime) {
                    rowValue.add(DateUtil.localDateTime2str((LocalDateTime) o));
                } else {
                    rowValue.add(o);
                }

            });
            values.add(rowValue);
        }
        return values;
    }

    /**
     * 构建查询条件
     *
     * @param dataItem
     * @param sql
     * @param zxCaseInfo
     */
    private static void buildQueryCondition(DataItem dataItem, StringBuilder sql, ZXCaseInfo zxCaseInfo) {
        sql.append(" from ");
        sql.append(dataItem.getTableName());
        sql.append(" where ");
        sql.append("ajbh = '");
        sql.append(zxCaseInfo.getAjbh());
        sql.append("' and fydm = '");
        sql.append(zxCaseInfo.getFydm());
        sql.append("' ");

        //  获取字段组成sql 获取数据  判断详情或者列表
        if (DataTypeEnum.DETAIL.getCode().equals(dataItem.getDataType())) {
            sql.append(" offset 0 rows fetch next 1 rows only ");
        }
    }

    /**
     * 构建查询字段
     *
     * @param dataFields
     * @param headers
     * @param headerMap
     * @param sql
     */
    private static void buildQueryFields(List<DataField> dataFields, List<String> headers, Map<String, String> headerMap, StringBuilder sql) {
        int i = 0;
        for (DataField dataField : dataFields) {
            headers.add(dataField.getFieldNameCn());
            headerMap.put(dataField.getFieldNameEn(), dataField.getFieldNameCn());
            sql.append(dataField.getFieldNameEn());
            if ((i++) != (dataFields.size() - 1)) {
                sql.append(", ");
            } else {
                sql.append(" \n");
            }
        }
    }

    @Override
    public void manageProcess(ProcessManageCmd cmd) {
        Process process = processConverter.cmd2do(cmd);
        this.update(process, Wrappers.<Process>lambdaQuery().eq(Process::getProcessId, process.getProcessId()));
    }

    @Override
    public void updateAllCaseStatus() {
        caseProcessService.caseProcessZX();
    }

    @Override
    public void updateSingleCaseStatus(SingleCaseUpdateCmd cmd) {
        DynamicDataSource dynamicDataSource = dynamicDataSourceMapper.selectOne(Wrappers.<DynamicDataSource>lambdaQuery().eq(DynamicDataSource::getDataSourceType, cmd.getDataSourceType()));
        ZXCaseInfo zxCaseInfo = DynamicDataSourceUtil.route(dynamicDataSource.getDynamicDataSourceId()
                , () -> zxCaseMapper.selectOne(Wrappers.<ZXCaseInfo>lambdaQuery().eq(ZXCaseInfo::getAh, cmd.getAh()).last(" offset 0 rows fetch next 1 rows only")));
        if(Objects.isNull(zxCaseInfo)){
            throw new RuntimeException("案件不存在");
        }
        caseProcessService.updateSingleCaseProcessStatus(zxCaseInfo);
    }

    /**
     * 校验流程节点及数据节点规则表达式
     *
     * @param processNodeCmds
     */
    private void validRuleExpression(List<ProcessNodeCmd> processNodeCmds) {
        // 1.校验流程节点
        List<RuleItemDTO> ruleItemDTOSProcess = newArrayList();
        for (ProcessNodeCmd processNodeCmd : processNodeCmds) { // 流程节点遍历
            List<ProcessNodeCmd.DataNodeCmd> dataNodeCmds = processNodeCmd.getDataNodes();
            for (ProcessNodeCmd.DataNodeCmd dataNodeCmd : dataNodeCmds) {//数据节点遍历
                // 数据节点校验
                validDataNode(processNodeCmd, dataNodeCmd);

                dataNodeCmd.getDataNodeStatuses()
                        .stream()
                        .filter(dns -> dns.getStatusId().equals(dataNodeCmd.getStatusId()))
                        .findAny()
                        .ifPresent(d -> ruleItemDTOSProcess.add(new RuleItemDTO(d.getOperationItem(), true)));
            }

            if (processNodeCmd.getFinishAll()) {// 需要完成所有节点 无需校验规则表达式
                continue;
            }
            // 流程节点校验
            try {
                RuleExpressionUtil.evalGroovy(processNodeCmd.getRuleExpression(), ruleItemDTOSProcess);
                ruleItemDTOSProcess.clear();
            } catch (Exception e) {
                log.error("规则校验失败 {}", processNodeCmd.getProcessNodeName(), e);
                throw new RuntimeException("流程 " + processNodeCmd.getProcessNodeName() + " 规则表达式不合法");
            }
        }
    }

    /**
     * 数据节点校验
     *
     * @param processNodeCmd
     * @param dataNodeCmd
     */
    private void validDataNode(ProcessNodeCmd processNodeCmd, ProcessNodeCmd.DataNodeCmd dataNodeCmd) {
        for (ProcessNodeCmd.DataNodeStatusCmd dataNodeStatusCmd : dataNodeCmd.getDataNodeStatuses()) {//单个数据节点所有状态遍历
            if (StringUtils.isBlank(dataNodeStatusCmd.getRuleExpression())) {
                continue;
            }
            List<RuleItemDTO> ruleItemDTOS = dataNodeStatusCmd.getNodeFields()
                    .stream()
                    .map(dn -> new RuleItemDTO(dn.getOperationItem(), true))
                    .collect(Collectors.toList());
            try {
                RuleExpressionUtil.evalGroovy(dataNodeStatusCmd.getRuleExpression(), ruleItemDTOS);
            } catch (Exception e) {
                log.error("规则校验失败 {} {}", processNodeCmd.getProcessNodeName(), dataNodeCmd.getDataNodeName(), e);
                throw new RuntimeException("流程 " + processNodeCmd.getProcessNodeName() + " 数据节点 " + dataNodeCmd.getDataNodeName() + " 规则表达式不合法");
            }
        }
    }

    /**
     * 校验流程名称是否重复
     *
     * @param processNodeCmds
     */
    private void validProcessName(List<ProcessNodeCmd> processNodeCmds) {
        long count = processNodeCmds
                .stream()
                .map(ProcessNodeCmd::getProcessNodeName)
                .distinct()
                .count();
        if (processNodeCmds.size() != count) {
            throw new RuntimeException("流程名称重复");
        }
    }
}




